package Game 
{
	import flash.display.Graphics;
	import flash.geom.Point;
	/**
	 * ...
	 * @author Vijay Ramani
	 */
	internal final class Sector 
	{
		private static var sectors:Array = null;
		private static var size:uint = 0;
		public static function InitSectors(_size:uint):void
		{
			sectors = new Array();
			size = _size;
			var _x:uint = 0;
			var _y:uint = 0;
			var _ind:int = 0;
			var _s:Sector = null;
			var _n:Sector = null;
			for (var r:uint = 0; r < _size; r++)
			{
				_y = MainGame.SECTOR_SIZE * (r + 0.5);
				for (var c:uint = 0; c < _size; c++)
				{
					_x = MainGame.SECTOR_SIZE * (c + 0.5);
					_s = new Sector(_x, _y);
					sectors.push(_s);
					
					_ind = GetSectorIndex(r-0, c-1);
					if (_ind >= 0)
					{
						_n = GetSector(_ind);
						_s.AddNeighbour(_n);
						_n.AddNeighbour(_s);
					}
					_ind = GetSectorIndex(r-1, c-0);
					if (_ind >= 0)
					{
						_n = GetSector(_ind);
						_s.AddNeighbour(_n);
						_n.AddNeighbour(_s);
					}
					_ind = GetSectorIndex(r-0, c+1);
					if (_ind >= 0)
					{
						_n = GetSector(_ind);
						_s.AddNeighbour(_n);
						_n.AddNeighbour(_s);
					}
					_ind = GetSectorIndex(r+1, c+0);
					if (_ind >= 0)
					{
						_n = GetSector(_ind);
						_s.AddNeighbour(_n);
						_n.AddNeighbour(_s);
					}
				}
			}
			var _dist:Number = 0;
			var _seci:Sector = null;
			var _secj:Sector = null;
			for (var _i:uint = 0; _i < sectors.length - 1; _i++)
			{
				_seci = (sectors[_i] as Sector);
				_seci.SetDistance(_i);
				for (var _j:uint = _i + 1; _j < sectors.length; _j++)
				{
					_secj = (sectors[_j] as Sector);
					_dist = (_seci.Origin.subtract(_secj.Origin).length);
					_seci.SetDistance(_j, _dist);
					_secj.SetDistance(_i, _dist);
				}
			}
		}
		
		private static function GetSectorIndex(_r:uint, _c:uint):int
		{
			if (_r < 0 || _r >= size) return -1;
			if (_c < 0 || _c >= size) return -1;
			
			var _i:uint = _r * size + _c;
			if (_i<0 || _i>=sectors.length) return -1;
			else return _i;
		}
		private static function GetSectorIndexFromPos(_x:Number, _y:Number):uint
		{
			var _c:uint = Math.floor(_x / MainGame.SECTOR_SIZE);
			var _r:uint = Math.floor(_y / MainGame.SECTOR_SIZE);
			
			return GetSectorIndex(_r, _c);
		}
		
		public static function AddToSectors(_obj:GameEntity):void
		{
			var _i:uint = GetSectorIndexFromPos(_obj.Pos.x, _obj.Pos.y);
			if (_i < 0) return; 
			var _sec:Sector = (sectors[_i] as Sector);
			_sec.Add(_obj);
			_obj.SectorRef = _sec;
		}
		public static function UpdateSectors(_obj:GameEntity):void
		{
			var _i:uint = GetSectorIndexFromPos(_obj.Pos.x, _obj.Pos.y);
			if (_i < 0) return; 
			var _sec:Sector = (sectors[_i] as Sector);
			if (_obj.SectorRef == _sec) return;
			
			_obj.SectorRef.Remove(_obj);
			_sec.Add(_obj);
			_obj.SectorRef = _sec;
		}
		
		private static function GetSector(_index:uint):Sector
		{
			return (sectors[_index] as Sector);
		}
		
		public static function DrawSectorGrid(_g:Graphics):void
		{
			_g.lineStyle(2, 0xFFFF00);
			var _s:Sector = null;
			for (var _i:uint = 0; _i < sectors.length; _i++)
			{
				_s = sectors[_i] as Sector;
				_g.drawRect(_s.Origin.x - MainGame.SECTOR_SIZE / 2, _s.Origin.y - MainGame.SECTOR_SIZE / 2, MainGame.SECTOR_SIZE, MainGame.SECTOR_SIZE);
			}
			_g.lineStyle();
		}
		
		private var origin:Point = null;
		private var objects:Array = null;
		private var distances:Array = null;
		private var effectiveEnergy:Number = 0;
		private var neighbours:Array = new Array();
		
		public function Sector(_x:uint = 0, _y:uint = 0) 
		{
			origin = new Point(_x,_y);
			objects = new Array();
			distances = new Array();
			effectiveEnergy = 0;
		}
		
		public function AddNeighbour(_sec:Sector):void
		{
			if (!_sec) return;
			neighbours.push(_sec);
		}
		
		private function CalculateSectorEnergy():void
		{
			var _entity:GameEntity = null;
			effectiveEnergy = 0;
			for (var i:uint = 0; i < objects.length; i++)
			{
				_entity = objects[i];
				effectiveEnergy += _entity.EnergyValue;
			}
		}
		
		public function Add(_obj:GameEntity):void
		{
			objects.push(_obj);
			CalculateSectorEnergy();
		}
		public function Remove(_obj:GameEntity):void
		{
			var _ind:int = objects.indexOf(_obj);
			if (_ind < 0) return;
			objects.splice(_ind, 1);
			CalculateSectorEnergy();
		}
		public function get Origin():Point
		{
			return origin;
		}
		
		public function SetDistance(_i:uint, _d:Number = 0):void
		{
			distances[_i] = _d;
		}
		
		public function get EffectiveEnergy():uint
		{
			return effectiveEnergy;
		}
		
		public function get Neighbours():Array
		{
			return neighbours;
		}
		
		private var chkSectors:Array = new Array();
		private var unchkSectors:Array = new Array();
		public var checkState:uint = 0;
		public function GetClosestEnergy(_obj:GameEntity):GameEntity
		{	
			unchkSectors.push(this);
			PopulateNeighbours(unchkSectors);
			var _g:GameEntity = ClosestEnergySectors(unchkSectors, _obj);
			if (!_g)
			{
				StripUsedSectors(unchkSectors);
				while (unchkSectors.length > 0)
				{
					PopulateNeighbours(unchkSectors);
					StripUsedSectors(unchkSectors);
					_g = ClosestEnergySectors(unchkSectors, _obj);
					if (_g) break;
				}
			}
			
			RestoreCheckState(chkSectors);
			RestoreCheckState(unchkSectors);
			return _g;
		}
		private function RestoreCheckState(_arr:Array):void
		{
			var _s:Sector = null;
			for (var i:uint = 0; i < _arr.length; i++)
			{
				_s = _arr[i] as Sector;
				_s.checkState = 0;
			}
			_arr.splice(0);
		}
		private function PopulateNeighbours(_arr:Array):void
		{
			var _lim:uint = _arr.length;
			var _s:Sector = null;
			var _n:Sector = null;
			var j:uint = 0;
			for (var i:uint = 0; i < _lim; i++)
			{
				_s = _arr[i] as Sector;
				_s.checkState = 1;
				for (j = 0; j < _s.Neighbours.length; j++)
				{
					_n = _s.Neighbours[j] as Sector;
					if (_n.checkState < 1) _arr.push(_n);
				}
			}
		}
		
		private function ClosestEnergySectors(_arr:Array, _obj:GameEntity):GameEntity
		{
			var _s:Sector = null;
			var _g:GameEntity = null;
			var _minEntity:GameEntity = null;
			var _min:Number = Infinity;
			var _d:Number = 0;
			for (var i:uint = 0; i < _arr.length; i++)
			{
				_s = _arr[i] as Sector;
				if (_s.EffectiveEnergy <= 0) continue;
				_g = _s.GetClosestEnergyInSector(_obj);
				if (!_g) continue;
				_d = _g.Pos.subtract(_obj.Pos).length;
				if (_d < _min)
				{
					_min = _d;
					_minEntity = _g;
				}
			}
			
			return _minEntity;
		}
		
		private function StripUsedSectors(_arr:Array):void
		{
			var _s:Sector = null;
			for (var i:Number = 0; i < _arr.length; i++)
			{
				_s = _arr[i] as Sector;
				if (_s.checkState > 0)
				{
					chkSectors.push(_s);
					_arr.splice(i, 1);
					i--;
				}
			}
		}
		
		public function GetClosestEnergyInSector(_obj:GameEntity):GameEntity
		{
			if (effectiveEnergy <= 0) return null;
			
			var _entity:GameEntity = null;
			var _minEntity:GameEntity = null;
			var _minValue:Number = Infinity;
			var _val:Number = 0;
			for (var i:uint = 0; i < objects.length; i++)
			{
				_entity = objects[i] as GameEntity;
				if (!_entity || _entity == _obj || _entity.EnergyValue <= 0) continue;
				
				_val = (_entity.Pos.subtract(_obj.Pos)).length;
				//if (_val > 0) _val = _entity.EnergyValue / (_val * _val);
				//else _val = 0;
				if (_val < _minValue) 
				{
					_minValue = _val;
					_minEntity = _entity;
				}
			}
			
			return _minEntity;
		}
	}

}